{ "cells": [ { "cell_type": "markdown", "source": [ "# Create a custom tile server\n", "\n", "cog_worker can be used to create a custom tile server that executes complex analyses on the fly.\n" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 1. Install dependencies\n", "\n", "We'll set up a simple server with `fastapi`. We also use `morecantile` to enable custom tile matrix sets, though using the default Manager parameters will give you a standard web mercator tiler." ], "metadata": {} }, { "cell_type": "code", "execution_count": 11, "source": [ "# if necessary\n", "#!pip3 install --quiet morecantile\n", "#!pip3 install --quiet fastapi[all]" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 2. Extend the Manager class\n", "\n", "Extend the cog_worker.Manager class to add a tiling function." ], "metadata": {} }, { "cell_type": "code", "execution_count": 12, "source": [ "from typing import Iterable, Mapping, Tuple, Any\n", "from cog_worker import Manager, WorkerFunction, BoundingBox\n", "from rasterio.crs import CRS\n", "import morecantile\n", "\n", "class TileManager(Manager):\n", " def __init__(self, *args, **kwargs):\n", " super().__init__(*args, **kwargs)\n", " self.tms = morecantile.TileMatrixSet.custom(\n", " self._proj_bounds, CRS.from_user_input(self.proj)\n", " )\n", "\n", " def tile(\n", " self,\n", " f: WorkerFunction,\n", " f_args: Iterable = None,\n", " f_kwargs: Mapping = None,\n", " z: int = 0,\n", " x: int = 0,\n", " y: int = 0,\n", " tilesize: int = 256,\n", " **kwargs\n", " ) -> Tuple[Any, BoundingBox]:\n", " \"\"\"Execute a function for the scale and bounds of a TMS tile.\n", "\n", " The tile method supports non-global and non-mercator tiling schemes via\n", " Morecantile. To generate standard web tiles, instantiate the Manager\n", " with the default parameters.\n", "\n", " Args:\n", " f (:obj:`cog_worker.types.WorkerFunction`): The function to execute. The function will\n", " recieve a cog_worker.worker.Worker as its first argument.\n", " f_args (list): Additional arguments to pass to the function.\n", " f_kwargs (dict): Additional keyword arguments to pass to the function.\n", " bounds (BoundingBox): The region to analize (default: self.bounds)\n", " max_size (int): The maximum size (width or height) in pixels to compute, ignoring any buffer\n", " (default: 1024px). Automatically reduces the scale of analysis to fit within `max_size`.\n", " **kwargs: Additional keyword arguments to overload the Manager's properties.\n", " (buffer).\n", "\n", " Returns:\n", " A tuple containing the return value of the function and the bounding\n", " box of the executed analysis in the target projection.\n", " \"\"\"\n", " # get the bounds of the tile\n", " proj_bounds = self.tms.xy_bounds(x, y, z)\n", " left, bottom, right, top = proj_bounds\n", "\n", " # set the scale to equal the tilesize\n", " size = max(right - left, top - bottom)\n", " scale = size / tilesize\n", "\n", " kwargs.update(\n", " {\n", " \"proj_bounds\": proj_bounds,\n", " \"scale\": scale,\n", " }\n", " )\n", "\n", " # execute the analysis as normal\n", " return self.execute(f, f_args, f_kwargs, **kwargs)" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 3. Create a FastAPI app\n", "\n", "Define an endpoint that executes a WorkerFunction for a tile." ], "metadata": {} }, { "cell_type": "code", "execution_count": 13, "source": [ "from fastapi import FastAPI, Response\n", "from rio_tiler.utils import render\n", "\n", "app = FastAPI()\n", "\n", "def read_cog(worker):\n", " return worker.read('example-cog.tif')\n", "\n", "functions = {\n", " 'read': read_cog\n", "}\n", "\n", "@app.get(\"/tile/{function}/{z}/{x}/{y}.png\")\n", "async def tile(\n", " function: str,\n", " z: int,\n", " x: int,\n", " y: int\n", "):\n", " f = functions[function]\n", " arr, bbox = TileManager().tile(f, z=z, x=x, y=y)\n", " img = render(arr)\n", " return Response(img, media_type=\"image/png\")" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 4. Run the app\n", "\n", " 1. Copy the code to a `main.py`.\n", " 2. Run the app with `uvicorn main:app --reload`\n", " 3. Check the endpoint at `http://localhost:8000/tile/read/0/0/0.png` 🎉\n", "\n", "Learn more about building a FastAPI app at https://fastapi.tiangolo.com." ], "metadata": {} } ], "metadata": { "orig_nbformat": 4, "language_info": { "name": "python", "version": "3.9.6", "mimetype": "text/x-python", "codemirror_mode": { "name": "ipython", "version": 3 }, "pygments_lexer": "ipython3", "nbconvert_exporter": "python", "file_extension": ".py" }, "kernelspec": { "name": "python3", "display_name": "Python 3.9.6 64-bit" }, "interpreter": { "hash": "d63b888f4c6be917599e44ea0181b24cda949ec28969e32a2270f090d5e6eb05" } }, "nbformat": 4, "nbformat_minor": 2 }